package com.hsh.tools;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/**
 * 日期转换辅助类
 *
 * @author 黄思海
 * @date 2018-02-07
 */
public abstract class DateUtils {

    private static final Logger logger = LoggerFactory.getLogger("DateUtils");

    public static final String FORMAT_DEFAULT = "yyyy-MM-dd HH:mm:ss";

    public static final String DATE_FORMAT_EN = "dd MMM,yyyy";
    public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";
    public static final String DATE_FORMAT_YYYY_MM_DD_HH_MI = "yyyy-MM-dd HH:mm";
    public static final String DATE_FORMAT_UTC_FULL = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ";
    public static final String DATE_FORMAT_UTC = "yyyy-MM-dd'T'HH:mm:ss";
    public static final String DATE_FORMAT_YYYY_MM_DD_SLASH = "yyyy/MM/dd";
    public static final String DATE_FORMAT_YYYY_MM = "yyyyMM";
    public static final String DATE_FORMAT_YYYY_MM_DD_HH_MM = "yyyy年MM月dd日 HH:mm";
    public static final String DATE_FORMAT_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";
    public static final String DATE_FORMAT_YYYY_MM_DD_CN = "yyyy年MM月dd日";
    public static final String DATE_FORMAT_MM_DD_HH_MI = "MM-dd HH:mm";
    public static final String DATE_FORMAT_MMDDHHMMSS = "MMddHHmmss";

    /**
     * 一天的毫秒数
     */
    public static final long ONE_DAY_MILLIS = 1000 * 60 * 60 * 24;


    /**
     * This method used for parse String to Date
     *
     * @param str
     * @param dateFormat
     * @return
     */
    public static Date parseDate(String str, String dateFormat) {
        if (str == null || str.trim().isEmpty()) {
            return null;
        }
        if (dateFormat == null || dateFormat.trim().isEmpty()) {
            dateFormat = FORMAT_DEFAULT;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        if (dateFormat.equalsIgnoreCase(DATE_FORMAT_UTC)) {
            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));//时区定义并进行时间获取
        }
        Date date = null;
        try {
            date = sdf.parse(str);
        } catch (ParseException e) {
            System.out.println("DateUtils.parseDate(" + str + ", " + dateFormat + ") ParseException");
        }
        return date;
    }

    /**
     * This method used for parse Date to String
     *
     * @param date
     * @param dateFormat
     * @return
     */
    public static String formatDate(Date date, String dateFormat) {
        if (dateFormat == null || dateFormat.trim().isEmpty()) {
            dateFormat = FORMAT_DEFAULT;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        return sdf.format(date);
    }

    /**
     * 增加日期中某类型的某数值。如增加日期
     *
     * @param date     日期
     * @param dateType 类型
     * @param amount   数值
     * @return 计算后日期
     * @deprecated refer to
     */
    @Deprecated
    public static Date addInteger(Date date, int dateType, int amount) {
        Date myDate = null;
        if (date != null) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(dateType, amount);
            myDate = calendar.getTime();
        }
        return myDate;
    }

    public static Date addInteger(Date date, int num, TimeUnit timeUnit) {
        if (date == null) {
            return null;
        }
        Assert.notNull(timeUnit, "TimeUnit is required");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        switch (timeUnit) {
            case MILLISECONDS:
                calendar.add(Calendar.MILLISECOND, num);
                break;
            case SECONDS:
                calendar.add(Calendar.SECOND, num);
                break;
            case MINUTES:
                calendar.add(Calendar.MINUTE, num);
                break;
            case HOURS:
                calendar.add(Calendar.HOUR, num);
                break;
            case DAYS:
                calendar.add(Calendar.DAY_OF_MONTH, num);
                break;
            default:
                logger.warn("unsupportted timeUnit={}", timeUnit);
                return date;
        }
        return calendar.getTime();
    }

    /**
     * 增减天数，并且截断时分秒，只保留日期
     * <p>
     * 2018-10-26 11:11:11,  +1 days, -> 2018-11-27 00:00:00
     *
     * @param date
     * @param days
     * @return
     */
    public static Date addDaysWithTruncate(Date date, Integer days) {
        if (date == null || days == null) {
            return date;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        cal.add(Calendar.DAY_OF_MONTH, days);
        return cal.getTime();
    }

    public static Date getFirstDayOfMonth(Date date, boolean truncate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        if (truncate) {
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
        }
        return cal.getTime();
    }

    /**
     * 下个月的第一天
     *
     * @param date
     * @param truncate
     * @return
     */
    public static Date getFirstDayofNextMonth(Date date, boolean truncate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MONTH, 1);
        if (truncate) {
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
        }
        return cal.getTime();
    }

    public static Date getLastDayofMonth(Date date, boolean truncate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MONTH, 1);
        cal.add(Calendar.DAY_OF_MONTH, -1);
        if (truncate) {
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
        }
        return cal.getTime();
    }

    /**
     * 获取当前年份
     *
     * @return
     */
    public static int getYear() {
        return Calendar.getInstance().get(Calendar.YEAR);
    }

    /**
     * 获取当前年份
     *
     * @return
     */
    public static int getYear(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return Calendar.getInstance().get(Calendar.YEAR);
    }

    /**
     * 获取当前月份
     *
     * @return
     */
    public static int getMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return Calendar.getInstance().get(Calendar.MONTH);
    }

    /**
     * 获取当前小时
     *
     * @return
     */
    public static int getHour(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return Calendar.getInstance().get(Calendar.HOUR);
    }

    /**
     * 获取当前分钟
     *
     * @return
     */
    public static int getMinute(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return Calendar.getInstance().get(Calendar.MINUTE);
    }

    /**
     * 获取当前年份
     *
     * @return
     */
    public static String getYearStr() {
        return String.format("%04d", getYear());
    }

    /**
     * 获取当前年份下一年
     *
     * @return
     */
    public static String getNextYearStr() {
        return String.format("%04d", getYear() + 1);
    }

    /**
     * 解析13位时间戳
     *
     * @param timestampStr 13位时间戳String
     * @param plusOneDay   是否需要加一天
     * @return
     */
    public static Date parseTimestampStr(String timestampStr, boolean plusOneDay) {
        if (timestampStr != null && !timestampStr.trim().isEmpty()) {
            Date temp = new Date(Long.parseLong(timestampStr));
            if (plusOneDay) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(temp);
                cal.add(Calendar.DAY_OF_MONTH, 1);
                return cal.getTime();
            } else {
                return temp;
            }
        }
        return null;
    }

    /**
     * 获得日期的str,  eg :  当前日期为20190101,   num传1,regex传"yyyy-MM-dd" , return -> 2019-01-02
     *
     * @param num   以当前日期为起始,正数即加天数,负数即减天数
     * @param regex 日期格式  eg:yyyyMMdd, yyyy-MM-dd等等
     * @return str
     */
    public static String getDataStr(int num, String regex) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, num);
        return new SimpleDateFormat(regex).format(calendar.getTime());
    }

    /**
     * 返回给定格式日期的毫秒值
     */
    public static String getDateJsonStr(String dateStr, String regex) {
        Date date = parseDate(dateStr, regex);
        return date == null ? null : date.getTime() + "";
    }

    private static final long ONE_DAY_TS = 1000 * 60 * 60 * 24;

    /**
     * 根据当前日期算，相差几天。
     * 未来的日期，比如明天：结果=1
     *
     * @param dateStrKt yyMMdd的格式（KT系统）
     * @return
     */
    public static long getBetweenDays(String dateStrKt) {
        if (StrUtils.isEmpty(dateStrKt)) {
            return -1;
        }
        Date date = DateUtils.parseDate(dateStrKt, "yyMMdd");
        if (date == null) {
            return -1;
        }
        long timeTs = date.getTime() - DateUtils.addDaysWithTruncate(new Date(), 0).getTime();
        return timeTs / ONE_DAY_TS;
    }


    public static Long getBetweenDays(String dateStrKt, String dateFormat) {
        if (StrUtils.isEmpty(dateStrKt)) {
            return null;
        }
        Date date = DateUtils.parseDate(dateStrKt, dateFormat);
        if (date == null) {
            return null;
        }
        long timeTs = DateUtils.addDaysWithTruncate(new Date(), 0).getTime() - date.getTime();
        return timeTs / ONE_DAY_TS;
    }

    public static String parserDate2YearMonth() {
        SimpleDateFormat sf = new SimpleDateFormat(DATE_FORMAT_YYYY_MM);
        Date d = new Date();
        return sf.format(d);
    }

    public static String parseDate2EnString(Date date) {
        SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_YYYY_MM_DD_HH_MM);
        return format.format(date);
    }

    public static Date parseTimestamp2Date(Long timestamp) {
        if (timestamp == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(FORMAT_DEFAULT);
        String format = formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()));
        return parseDate(format, FORMAT_DEFAULT);
    }

    /**
     * 获取上个月开始时间和结束时间
     *
     * @return
     */
    public static Map<String, Date> getLastMonthTime() {
        Long startTime = getLastMonthStartTime();
        Long endTime = getLastMonthEndTime();
        Map<String, Date> map = new HashMap<>();
        map.put("startDate", parseTimestamp2Date(startTime));
        map.put("endDate", parseTimestamp2Date(endTime));
        return map;
    }

    public static Long getLastMonthStartTime() {
        long currentTime = System.currentTimeMillis();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);// 设置为1号,当前日期既为本月第一天
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTimeInMillis();
    }


    public static Long getLastMonthEndTime() {
        long currentTime = System.currentTimeMillis();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));// 获取当前月最后一天
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);

        return calendar.getTimeInMillis();
    }

    public static Boolean isSameMonth(Date date1, Date date2) {
        if (!isSameYear(date1, date2)) {
            return false;
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
    }

    public static Boolean isSameYear(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
    }

    /**
     * 根据时间 和时间格式 校验是否正确
     *
     * @param length 校验的长度
     * @param sDate  校验的日期
     * @param format 校验的格式
     * @return
     */
    public static boolean isLegalDate(int length, String sDate, String format) {
        if (sDate == null || sDate.length() != length) {
            return false;
        }
        DateFormat formatter = new SimpleDateFormat(format);
        try {
            Date date = formatter.parse(sDate);
            return sDate.equals(formatter.format(date));
        } catch (Exception e) {
            return false;
        }
    }
}
